/*
Matthew Parker
CPE 301 - Design Assignment 8
5/07/2014
*/

#define F_CPU 8000000
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h> // for storing memory

//Store look up table for sin wave inside of memory
static const uint8_t sin_table[] PROGMEM =
{
	40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,41,41,41,41,41,41,41,41,42,42,
	42,42,42,42,42,43,43,43,43,43,44,44,44,44,45,45,45,46,46,46,46,47,47,48,48,48,49,49,50,50,51,51,
	52,52,53,54,54,55,56,57,58,58,59,61,62,63,64,65,67,69,70,72,74,77,79,82,85,89,94,99,105,112,122,135,
	152,180,235
};

//Global variable used for DAC value output
volatile uint16_t global_DAC = 0;

void start_ADC(){
	//This function starts the ADC
	ADCSRA |= 1<<ADEN;
	ADMUX = 0b01000000;
	ADCSRA |= 1<<ADSC;
}

void start_Timers(){
	//This function starts the timers
	TCCR1A = 0b00000000;
	TCCR1B = 0b00001001;
	TIMSK1 = 0b00000010;
	OCR1A = 250;
}

void init_TWI(){
	//This function sets the max speed for TWI
	TWBR = 0;
	TWSR = 0;
}

uint8_t wait_TWI(){
	//This function waits for TWI to finish and returns the TWI Status
	while(!(TWCR & (1<<TWINT))){
		//Wait for TWI to finish.
	}
	return (TWSR&0xF8);
}

uint8_t send_TWI(uint8_t data){
	//This function sends data to TWI
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN); // enable
	return wait_TWI(); // wait for completion and return status
}

uint8_t start_transfer(){
	//This function starts the transfer for the MCP4725
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
	if(wait_TWI() != 0x08)
		return 1; //Error 1
	if(send_TWI(0xC0) != 0x18)
		return 2; // Error 2
	return 0; // no Error
}

void set_TWI(uint8_t ms, uint8_t ls){
	//This function sets ms and ls
	if(send_TWI(ms&0xF)!=0x28){
		return;
	}
	if(send_TWI(ls)!=0x28){
		return;
	}
}

//These variables are only used by the following ISR
//They are made global to preserve their value
volatile uint8_t sine_phase = 0; // current phase of the sine wave
volatile uint8_t trans_counter = 0; //number of ticks since the last transition
volatile uint8_t raw_DAC = 0; //value of the DAC from the input
volatile uint8_t trans_inc = 20; //the number of ticks to send when interrupted

ISR(TIMER1_COMPA_vect){
	//Interrupt Service Routine for the Timer1 CTC compare A
	uint8_t delta; // sine wave phase adjuster
	uint16_t temp_DAC;
	if(!(ADCSRA&(1<<ADSC))){	//Checks if the ADC has finished
		trans_inc = 8+(ADC>>4);	//The number of ticks controls the frequency
		start_ADC(); //Reads in potentiometer value in order to change frequency of sine wave
	}
	//Adjust sine wave phase
	delta = pgm_read_word(&sin_table[(sine_phase&1)?98-raw_DAC:raw_DAC]); // sine table read
	if( (trans_counter+trans_inc < delta ) && (trans_counter < delta-trans_inc) ){
		trans_counter += trans_inc;
	} else {
		trans_counter += trans_inc;
		while(trans_counter > delta) {
			trans_counter -= delta;
			raw_DAC++;
			if(raw_DAC >= 99){
				// update phase when needed
				raw_DAC = 0;
				sine_phase = (sine_phase+1)&3;
			}
			//Sine inversion
			delta = pgm_read_word(&sin_table[(sine_phase&1)?98-raw_DAC:raw_DAC]); // sine table read
		}
		temp_DAC = raw_DAC;
		switch(sine_phase){
			// transform the DAC value
			case 0: temp_DAC += 99; break;
			case 1: temp_DAC = 198-temp_DAC; break;
			case 2: temp_DAC = 99-temp_DAC; break;
		}
		temp_DAC <<= 4;
		global_DAC = temp_DAC;
	}
}

int main(void){
	start_ADC();
	start_Timers();
	sei(); // enable interrupts
	init_TWI();
	start_transfer();
	uint8_t DAC_value_high, DAC_value_low;
	while(1){
		//Continuously outputs to MCP
		cli(); //Atomically read from the global DAC value
		DAC_value_high = (uint8_t)((global_DAC>>8)&0x0F);
		DAC_value_low = (uint8_t)(global_DAC&0xFF);
		sei();
		set_TWI(DAC_value_high,DAC_value_low);
	}
}